#include <fstream>
#include <map>
#include <boost/filesystem.hpp>

#include "map/q3bsp.h"

using boost::filesystem::path;

using std::ifstream;
using std::map;

#include "core/logger.h"
#include "resources/texturemanager.h"

namespace engine {
	
	namespace map {

		using engine::resources::TextureManager;
		
		Q3BSP::Q3BSP(const string& name):
		WorldMesh(name) {
			Logger::getLogger("map")->setLevel(LL_DEBUG);
		}
		
		Q3BSP::~Q3BSP() {
			
		}
		
		bool Q3BSP::loadMap(const string& filename) {
			Logger::getLogger("map")->debug("Loading BSP file: " + filename);
			
			uint numVertices;
			uint numFaces;
			uint numTextures;
			uint numLightmaps;
			
			stQ3Header header;
			vector<stQ3FileLump> lumps(iMaxLumpsOffset);
			
			
			ifstream fileIn;
			Logger::getLogger("map")->debug(" - Opening file");
			
			fileIn.open(filename.c_str(), std::ios::binary);
			if (!fileIn.is_open()) {
				Logger::getLogger("map")->error("Could not load BSP file");
				return false;
			}
			Logger::getLogger("map")->debug(" - File open");
			
			fileIn.read(reinterpret_cast<char*>(&header), sizeof(stQ3Header));
			
			Logger::getLogger("map")->debug(" - Loaded header");
			
			if (string(header.bspID) != "IBSP.") {
				Logger::getLogger("map")->error("BSP file header is not valid");
				return false;
			}
			
			fileIn.read(reinterpret_cast<char*>(&lumps[0]), sizeof(stQ3FileLump) * iMaxLumpsOffset);
			
			Logger::getLogger("map")->debug(" - Loaded lumps");
			
			numVertices = lumps[iVerticesOffset].length / sizeof(stQ3Vertex);
			
			//Allocate memory for the vertices
			vector<stQ3Vertex> vertices(numVertices);
				
			fileIn.seekg(lumps[iVerticesOffset].offset, std::ios_base::beg);
			fileIn.read(reinterpret_cast<char*>(&vertices[0]), numVertices * sizeof(stQ3Vertex));
			
			Logger::getLogger("map")->debug(" - Loaded vertices");
			
			numFaces = lumps[iFacesOffset].length / sizeof(stQ3Face);
			
			//allocate memory for faces
			vector<stQ3Face> faces(numFaces);
	
			fileIn.seekg(lumps[iFacesOffset].offset, std::ios_base::beg);
			fileIn.read(reinterpret_cast<char*>(&faces[0]), numFaces * sizeof(stQ3Face));
			
			Logger::getLogger("map")->debug(" - Loaded faces");
			
			numTextures = lumps[iTextureOffset].length / sizeof(stQ3Texture);
			//allocate memory for faces
			vector<stQ3Texture> textures(numTextures);
			fileIn.seekg(lumps[iTextureOffset].offset, std::ios_base::beg);
			fileIn.read(reinterpret_cast<char*>(&textures[0]), numTextures * sizeof(stQ3Texture));			
			
			Logger::getLogger("map")->debug(" - Loaded textures");
			
			numLightmaps = lumps[iLightMapsOffset].length / sizeof(stQ3LightMap);
			
			vector<stQ3LightMap> lightmaps(numLightmaps);
			fileIn.seekg(lumps[iLightMapsOffset].offset, std::ios_base::beg);
			fileIn.read(reinterpret_cast<char*>(&lightmaps[0]), numLightmaps * sizeof(stQ3LightMap));			
			
			fileIn.close();
			
			storeBSPData(vertices, faces, textures, lightmaps);
			return true;
		}
		
		void Q3BSP::storeBSPData( const vector<stQ3Vertex>& vertices,
								  const vector<stQ3Face>& faces,
								  const vector<stQ3Texture>& textures,
								  const vector<stQ3LightMap>& lightmaps) {
								
			for(vector<stQ3Vertex>::const_iterator it = vertices.begin();
					it != vertices.end(); ++it) {
			
				WorldVertex newVert;
		   
				newVert.m_Pos.m_X = (*it).vertex[0];
				newVert.m_Pos.m_Y = (*it).vertex[2];
				newVert.m_Pos.m_Z = -(*it).vertex[1];
				
				m_VertexData.push_back(newVert);
			}

			for(vector<stQ3Face>::const_iterator it = faces.begin();
					it != faces.end(); ++it) {
			
				//At the moment only handle normal polygons
				if ((*it).type != 1) {
					continue;
				}
				
				int end = (*it).startVertexIndex + (*it).totalVertices;
				
				for (int i = (*it).startVertexIndex; i < end - 1; i++) {
					WorldTriangle newTriangle;
					newTriangle.m_Index[0] = (*it).startVertexIndex;
					newTriangle.m_Index[1] = i + 1;
					newTriangle.m_Index[2] = i;
					
					newTriangle.m_TexCoords[0] = vertices[newTriangle.m_Index[0]].texCoord;
					newTriangle.m_TexCoords[1] = vertices[newTriangle.m_Index[1]].texCoord;
					newTriangle.m_TexCoords[2] = vertices[newTriangle.m_Index[2]].texCoord;
					
					newTriangle.m_TextureName = path(textures[(*it).texID].file).leaf();
					
					//std::cout << newTriangle.m_TextureName << std::endl;
					
					slVec3 vec1, vec2;
					
					vec1 = m_VertexData[newTriangle.m_Index[1]].m_Pos - m_VertexData[newTriangle.m_Index[0]].m_Pos;
					vec2 = m_VertexData[newTriangle.m_Index[2]].m_Pos - m_VertexData[newTriangle.m_Index[0]].m_Pos;
					slVec3Cross(&newTriangle.m_FaceNormal, &vec1, &vec2);
					slVec3Normalize(&newTriangle.m_FaceNormal, &newTriangle.m_FaceNormal);
					
					
					m_TriangleData.push_back(newTriangle);
				}
			}
						
			for(vector<stQ3Texture>::const_iterator it = textures.begin();
					it != textures.end(); ++it) {
				
				//generate a texture name
				//
				string file = path((*it).file).leaf();
				
				if (file == "noshader") continue;
				
				string filePath = (path("materials") / file).string() + ".xml";
				std::cout << file << std::endl;
				
				getMaterialManager()->loadResourceFromFile(file, filePath);
			}
								  	
		}
		
		void Q3BSP::unloadMap() {
			
		}
	}
}
